package com.wtf.generator.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wtf.generator.common.utils.GeneratorUtils;
import com.wtf.generator.common.utils.PageUtils;
import com.wtf.generator.common.utils.SqlResolver;
import com.wtf.generator.constants.Constant;
import com.wtf.generator.constants.TemplateDefaultConfigConstant;
import com.wtf.generator.entity.WtfTemplateConfig;
import com.wtf.generator.enums.SqlResponseMapper;
import com.wtf.generator.enums.TableApi;
import com.wtf.generator.executor.DsDbExecutor;
import com.wtf.generator.model.DbSql;
import com.wtf.generator.model.dto.CodeDTO;
import com.wtf.generator.model.dto.DsSqlDTO;
import com.wtf.generator.model.dto.TableDTO;
import com.wtf.generator.model.TemplateDefaultDynamicConfig;
import com.wtf.generator.model.vo.*;
import com.wtf.generator.service.DbTableSerivce;
import com.wtf.generator.service.DynamicRoutingDataSourceService;
import com.wtf.generator.service.WtfTemplateConfigService;
import com.wtf.generator.support.DbTableSqlSupport;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

/**
 * date: 2022/6/18 10:59
 * @author linhuanyou
 * @email 496558736@qq.com
 * @version 1.0
 */
@Slf4j
@Service
public class DbTableSerivceImpl implements DbTableSerivce {
    @Autowired
    private WtfTemplateConfigService wtfTemplateConfigService;

    @Autowired
    private DynamicRoutingDataSourceService dynamicRoutingDataSourceService;

    @Autowired
    private DsDbExecutor dsDbExecutor;

    @Autowired
    private DbTableSqlSupport dbTableSqlSupport;

    public static final String TABLE_NAME_FIELD = "#{tableName}";

    @Value("${spring.datasource.dynamic.primary}")
    private String primaryDs;

    @Override
    public PageUtils page(TableDTO tableDTO) throws Exception {
        if(StringUtils.isBlank(tableDTO.getDs())){
            tableDTO.setDs(primaryDs);
        }
        DsVO dsVO = dynamicRoutingDataSourceService.list(tableDTO.getDs()).stream().findFirst().get();
        List<DsTableVO> tableVOS = new ArrayList<>();
        DbType dbType = dsVO.getDbType();
        String tableListSql = dbTableSqlSupport.execSql(dbType, TableApi.TABLElIST);
        Map<String,String> params = new HashMap<>();
        params.put(TABLE_NAME_FIELD,tableDTO.getTableName());
        params.put(Constant.PAGE,String.valueOf(tableDTO.getPage()));
        params.put(Constant.LIMIT,String.valueOf(tableDTO.getLimit()));
        DbSql dbSql = SqlResolver.obtainSql(params, tableListSql, dbType);
        DsSqlDTO dsSqlDTOList = new DsSqlDTO();
        dsSqlDTOList.setExcSql(dbSql.getExcSql());
        dsSqlDTOList.setDbType(dbType);
        dsSqlDTOList.setDs(dsVO.getDs());
        dsSqlDTOList.setTableApi(TableApi.TABLElIST);
        dsSqlDTOList.setSqlResponseMapper(SqlResponseMapper.MAP_LIST);
        List<TableVO> table = dsDbExecutor.doExecutor(dsSqlDTOList,new TypeReference<List<TableVO>>(){}).stream().map(e->{
            e.setDs(dsVO.getDs());
            return e;
        }).collect(Collectors.toList());

        DsSqlDTO dsSqlDTOCount = new DsSqlDTO();
        dsSqlDTOCount.setExcSql(dbSql.getCountSql());
        dsSqlDTOCount.setDbType(dbType);
        dsSqlDTOCount.setDs(dsVO.getDs());
        dsSqlDTOCount.setTableApi(TableApi.COUNT);
        dsSqlDTOCount.setSqlResponseMapper(SqlResponseMapper.T);
        Integer total = dsDbExecutor.doExecutor(dsSqlDTOCount, new TypeReference<Integer>() {
        });
        return new PageUtils(table, total, tableDTO.getLimit(), tableDTO.getPage());
    }

    @Override
    public byte[] generatorCode(CodeDTO codeDTO) throws Exception {
        TemplateDefaultDynamicConfig templateDynamicConfigVO = getTemplate(codeDTO.getDs(),codeDTO.getTemplateId());
        if(StringUtils.isBlank(templateDynamicConfigVO.getTablePrefix())) {
            templateDynamicConfigVO.setTablePrefix("") ;
        };
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableName : codeDTO.getTableNames()) {
            TableDTO tableDTO = new TableDTO();
            tableDTO.setDs(codeDTO.getDs());
            tableDTO.setTableName(tableName);
            //查询表信息
            TableVO table = queryTable(tableDTO);
            //查询列信息
            List<ColumnVO> columns = queryColumns(tableDTO);
            //生成代码
            GeneratorUtils.generatorCode(table, columns, templateDynamicConfigVO,zip);
        }
        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }

    private TemplateDefaultDynamicConfig getTemplate(String ds, Integer templateId) {
        if(templateId == null){
            return defaultTemplate();
        }
        LambdaQueryWrapper<WtfTemplateConfig> wtfTemplateConfigLambdaQueryWrapper = new LambdaQueryWrapper<>();
        wtfTemplateConfigLambdaQueryWrapper.eq(WtfTemplateConfig::getId,templateId);
        wtfTemplateConfigLambdaQueryWrapper.eq(WtfTemplateConfig::getDs,ds);
        WtfTemplateConfig wtfTemplateConfig = wtfTemplateConfigService.getBaseMapper().selectOne(wtfTemplateConfigLambdaQueryWrapper);
        if(wtfTemplateConfig == null){
            return defaultTemplate();
        }
        TemplateDefaultDynamicConfig templateDefaultDynamicConfig = new TemplateDefaultDynamicConfig();
        BeanUtils.copyProperties(wtfTemplateConfig, templateDefaultDynamicConfig);
        return templateDefaultDynamicConfig;
    }

    private TemplateDefaultDynamicConfig defaultTemplate() {
        TemplateDefaultDynamicConfig templateDynamicConfigVO = new TemplateDefaultDynamicConfig();
        templateDynamicConfigVO.setAuthor(TemplateDefaultConfigConstant.AUTHOR);
        templateDynamicConfigVO.setPackageReference(TemplateDefaultConfigConstant.PACKAGE_REFERENCE);
        templateDynamicConfigVO.setModuleName(TemplateDefaultConfigConstant.MODULE_NAME);
        templateDynamicConfigVO.setEmail(TemplateDefaultConfigConstant.EMAIL);
        return templateDynamicConfigVO;
    }

    private TableVO queryTable(TableDTO tableDTO) throws Exception {
        DsVO dataSource = dynamicRoutingDataSourceService.list(tableDTO.getDs()).stream().findFirst().get();
        DbType dbType = dataSource.getDbType();
        String tableSql = dbTableSqlSupport.execSql(dbType, TableApi.TABLE);
        Map<String,String> params = new HashMap<>();
        params.put(TABLE_NAME_FIELD,tableDTO.getTableName());
        DsSqlDTO dsSqlDTO = new DsSqlDTO();
        dsSqlDTO.setExcSql(SqlResolver.obtainSql(params,tableSql,dbType).getExcSql());
        dsSqlDTO.setDbType(dbType);
        dsSqlDTO.setDs(dataSource.getDs());
        dsSqlDTO.setTableApi(TableApi.TABLE);
        dsSqlDTO.setSqlResponseMapper(SqlResponseMapper.MAP);
        TableVO table = dsDbExecutor.doExecutor(dsSqlDTO,new TypeReference<TableVO>(){});
        return table;
    }

    private List<ColumnVO> queryColumns(TableDTO tableDTO) throws Exception {
        DsVO dataSource = dynamicRoutingDataSourceService.list(tableDTO.getDs()).stream().findFirst().get();
        DbType dbType = dataSource.getDbType();
        String columnsSql = dbTableSqlSupport.execSql(dbType, TableApi.COLUMNS);
        Map<String,String> params = new HashMap<>();
        params.put(TABLE_NAME_FIELD,tableDTO.getTableName());
        DsSqlDTO dsSqlDTO = new DsSqlDTO();
        dsSqlDTO.setExcSql(SqlResolver.obtainSql(params,columnsSql,dbType).getExcSql());
        dsSqlDTO.setDbType(dbType);
        dsSqlDTO.setDs(dataSource.getDs());
        dsSqlDTO.setTableApi(TableApi.COLUMNS);
        dsSqlDTO.setSqlResponseMapper(SqlResponseMapper.MAP_LIST);
        List<ColumnVO> columns = dsDbExecutor.doExecutor(dsSqlDTO,new TypeReference<List<ColumnVO>>(){});
        return columns;
    }
}
